<?php
/**
 * websocket 即时通讯
 * User: JZ浩
 * Date: 2018/6/5 0031
 * Time: 13:40
 */

namespace app\websocket\controller;

use think\swoole\Server as swoole_server;

class Server extends swoole_server{
    // 监听所有地址
    protected $host = '0.0.0.0';
    //监听 9501 端口
    protected $port = 9501;
    //指定运行模式为多进程
    protected $mode = SWOOLE_PROCESS;
    protected $serverType = 'socket';
    // 指定 socket 的类型为 ipv4 的 tcp socket
    protected $sockType = SWOOLE_SOCK_TCP;
    //配置项
    protected $option = [
        /**
         *  设置启动的worker进程数
         *  业务代码是全异步非阻塞的，这里设置为CPU的1-4倍最合理
         *  业务代码为同步阻塞，需要根据请求响应时间和系统负载来调整
         */
        'worker_num' => 2,
        // 守护进程化
        'daemonize'  => false,
        // 监听队列的长度
        'backlog'    => 128
    ];

    private $redis;

    public function __construct(){
        parent::__construct();

        swoole_set_process_name("admin_websocket");

        //redis() = redis();
    }

    /**
     * 握手连接
     * @param $server
     * @param $request
     */
    public function onOpen($server, $request){
        //redis()->setFD('userChat',$request->fd);
        echo "server: handshake success with fd{$request->fd}\n";
    }


    /*
     * 获取用户连接ID
     */
    private function __getOpenId(){
        $userStr = redis()->get('userList');
        $userArr = json_decode($userStr,true);
        return $userArr?:$userArr;
    }

    /**
     * 定时执行广播推送
     * @param $server
     * @param $workerId
     */
    public function onWorkerStart($server, $workerId){

        if ($workerId == 0) {
            # 定时推送数据
//            $server->tick(10000, function() use ($server) {
//                $result = $this->getOrder();
//                if($result > 0) {
//                    foreach ($server->connections as $fd) {
//                        $server->push($fd, json_encode(['data' => ['count' => $result], 'code' => 200, 'message' => 'getOrder']));
//                    }
//                }
//            });

            //定时一小时，执行删除失效的设备信息
//            $server->tick(1000*60*60, function() use ($server) {
//                $fds = redis()->getFD();
//                if($fds) {
//                    //存在在线用户才进行推送
//                    foreach ($fds as $fd) {
//                        //校验fd是否存在
//                        $res_fd = $this->swoole->exist($fd);
//                        if(!$res_fd) {
//                            redis()->delFD($fd);
//                        }
//                    }
//                }
//            });
        }
    }

    /**
     * 发送消息（客户端触发）
     * @param $server
     * @param $frame
     */
    public function onMessage($server, $frame){
        $data = json_decode($frame->data,true);
        switch ($data['type']){
            case 'ping':
                $this->swoole->push($frame->fd,json_encode(['data' => [], 'code' => 200, 'message' => 'ping']));
                break;
            case 'login':
                //创建新在线fd
                $this->__setOpenId($frame->fd);
                $userList = redis()->smembers('user_chat');
                $this->__setUserName($frame->fd,$data['user']);
                $this->__onlinePushAll($userList,$data);
                /*$result = $this->getCount();
                if($result == false) {
                    $result = [
                        'order_count' => 0,
                        'account_count' => 0,
                    ];
                }
                $admin_ids = $this->getAdminIds();
                foreach ($admin_ids as $admin_id){
                    $fds = redis()->smembers($admin_id);
                    //$result['total_notice'] = $this->getAdminNotice($admin_id);
                    if($fds){
                        foreach ($fds as $fd){
                            //校验fd是否存在
                            $res_fd = $this->swoole->exist($fd);
                            if($res_fd) {
                                $this->swoole->push($fd, json_encode(['data' => $data['content'], 'code' => 200, 'message' => 'push']));
                            }else{
                                redis()->srem($admin_id,$fd);
                            }
                        }

                    }
                }*/

                break;
            case 'close':
                redis()->srem('user_chat',$frame->fd);//删除失效的fd
                break;
            case 'msg':
                $userList = redis()->smembers ('user_chat');
                $this->__onlinePushAll($userList,$data);
                break;
        }
    }

    //用户名存储
    private function __setUserName($fd,$name){
        $str = 'fd';
        $isUserKey = redis()->exists('userName');
        $userList = [];
        if($isUserKey){
            $userKey = redis()->get('userName');
            $userList = unserialize($userKey);
            if(array_key_exists($str.$fd,$userList)){
                unset($userList[$str.$fd]);
                $newArr = [$str.$fd=>$name];
            }else{
                $newArr = [$str.$fd=>$name];
            }
        }
        else{
            $newArr = [$str.$fd=>$name];
        }
        $result = array_merge($newArr,$userList);
        $userKey = serialize($result);
        redis()->set('userName',$userKey);
    }

    //获取用户名
    private function __getUserName($fd){
        $isUserKey = redis()->exists('userName');
        if($isUserKey){
            $userKey = redis()->get('userName');
            $userList = unserialize($userKey);
            if(array_key_exists('fd'.$fd,$userList)){
                redis()->sremove('user_chat',$fd);
                return $userList['fd'.$fd];
            }
        }
        return false;
    }

    /*
     * 存连接ID
     */
    private function __setOpenId($fd){
        $userArr = redis()->sismember('user_chat',$fd);
        if($userArr){
            redis()->sremove('user_chat',$fd);
            redis()->sadd('user_chat',$fd);
        }else{
            redis()->sadd('user_chat',$fd);
        }
    }


    //推送全部
    private function __onlinePushAll($userArr,$data){
        if($userArr){
            foreach ($userArr as $k =>$v){
                if($this->swoole->exist($v)){
                    $this->swoole->push($v, json_encode(['data'=>$data['content'],'user'=>$data['user'],'code'=>200,'msg'=>'msg']));
                }else{
                    redis()->sremove('user_chat',$v);
                }
            }
        }
    }

    /**
     * 发送消息（链接触发）
     * @param $server
     * @param $frame
     */
    public function onRequest($server, $frame){

        $result = $this->getCount();
        if($result == false) {
           $result = [
               'order_count' => 0,
               'account_count' => 0,
           ];
        }
        $admin_ids = $this->getAdminIds();
        foreach ($admin_ids as $admin_id){
            $fds = redis()->smembers($admin_id);
            //$result['total_notice'] = $this->getAdminNotice($admin_id);
            if($fds){
                foreach ($fds as $fd){
                    //校验fd是否存在
                    $res_fd = $this->swoole->exist($fd);
                    if($res_fd) {
                        $this->swoole->push($fd, json_encode(['data' => $result, 'code' => 200, 'message' => 'push']));
                    }else{
                        redis()->srem($admin_id,$fd);
                    }
                }

            }
        }
    }

    /**
     * 退出
     * @param $server
     * @param $fd
     */
    public function onClose($server, $fd){
        $res = $this->__getUserName($fd);
        if($res){
            $userList = redis()->smembers('user_chat');
            $this->__onlinePushAll($userList,['user'=>$res,'content'=>'下线了~~~']);
        }
        echo "client {$fd} closed\n";
    }

    public function getCount(){
        $common = new \app\common\controller\CommonBase();
        $result = $common->pushAdmin();
        return $result;
    }

    /**
     * 获取管理员未读消息个数
     * @param $admin_id
     * @return int
     */
    public function getAdminNotice($admin_id){
        $common = new \app\common\controller\CommonBase();
        $notice = $common->getAdminNotice($admin_id);
        return $notice;
    }

    public function getAdminIds(){
        $common = new \app\common\controller\CommonBase();
        $admin_ids = $common->getAdminLogin();
        return $admin_ids;
    }

}